<template>
  <div class="vue-topology" :style="{ width, height }">
    <div class="topology-canvas" ref="canvas" @mousedown="handleMouseDown">
      <!-- 节点容器 -->
      <div class="nodes-container">
        <div
          v-for="node in graphNodes"
          :key="node.id"
          :class="['node', node.type, { selected: node.selected }]"
          :style="{
            left: `${node.x}px`,
            top: `${node.y}px`,
            width: `${node.width}px`,
            height: `${node.height}px`,
            backgroundColor: node.color
          }"
          @mousedown.stop="handleNodeMouseDown($event, node)"
        >
          <div class="node-content">
            <slot name="node-content" :node="node">
              {{ node.label || `Node ${node.id}` }}
            </slot>
          </div>
          <!-- 节点锚点 -->
          <div
            v-for="anchor in node.anchors"
            :key="anchor.id"
            :class="['anchor', { active: anchor.active }]"
            :style="{
              left: `${anchor.x}px`,
              top: `${anchor.y}px`
            }"
            @mousedown.stop="handleAnchorMouseDown($event, node, anchor)"
          ></div>
        </div>
      </div>

      <!-- 边容器 -->
      <svg class="edges-container" :width="width" :height="height">
        <g v-for="edge in graphEdges" :key="edge.id">
          <path
            :d="edge.path"
            :stroke="edge.selected ? '#1890ff' : edge.color"
            :stroke-width="edge.selected ? 3 : edge.width"
            fill="none"
            @mousedown.stop="handleEdgeMouseDown($event, edge)"
          />
          <circle
            v-if="edge.showArrow"
            :cx="edge.arrowHead.x"
            :cy="edge.arrowHead.y"
            :r="5"
            :fill="edge.selected ? '#1890ff' : edge.color"
          />
        </g>
      </svg>
    </div>
  </div>
</template>

<script>
import { Graph, Node, Edge, Anchor } from '../core'
import { throttle } from '../utils/helper'

export default {
  name: 'Topology',
  props: {
    width: {
      type: [Number, String],
      default: '100%'
    },
    height: {
      type: [Number, String],
      default: '100%'
    },
    nodes: {
      type: Array,
      default: () => []
    },
    edges: {
      type: Array,
      default: () => []
    },
    gridSize: {
      type: Number,
      default: 20
    },
    // 添加新属性控制网格对齐
    snapToGrid: {
      type: Boolean,
      default: true
    },
    readOnly: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      graph: null,
      draggingNode: null,
      draggingEdge: null,
      startAnchor: null,
      isPanning: false,
      panStart: { x: 0, y: 0 },
      scale: 1,
      // 添加这两个属性来同步Graph数据
      graphNodes: [],
      graphEdges: []
    }
  },
  watch: {
    nodes: {
      handler(newNodes) {
        if (this.graph) {
          this.graph.setNodes(newNodes)
          // 同步到graphNodes
          this.graphNodes = Array.from(this.graph.nodes.values())
        }
      },
      deep: true
    },
    edges: {
      handler(newEdges) {
        if (this.graph) {
          this.graph.setEdges(newEdges)
          // 同步到graphEdges
          this.graphEdges = Array.from(this.graph.edges.values())
        }
      },
      deep: true
    }
  },
  mounted() {
    // 初始化图形
    this.graph = new Graph({
      container: this.$refs.canvas,
      nodes: this.nodes,
      edges: this.edges
    })

    // 初始同步数据
    this.graphNodes = Array.from(this.graph.nodes.values())
    this.graphEdges = Array.from(this.graph.edges.values())

    // 添加Graph事件监听
    this.graph.on('nodeAdded', () => {
      this.graphNodes = Array.from(this.graph.nodes.values())
    })

    this.graph.on('nodeRemoved', () => {
      this.graphNodes = Array.from(this.graph.nodes.values())
    })

    // 添加边相关事件监听
    this.graph.on('edgeAdded', () => {
      this.graphEdges = Array.from(this.graph.edges.values())
    })

    this.graph.on('edgeRemoved', () => {
      this.graphEdges = Array.from(this.graph.edges.values())
    })

    // 绑定窗口大小变化事件
    window.addEventListener('resize', throttle(this.handleResize, 100))
    this.handleResize()
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.handleResize)
  },
  methods: {
    handleResize() {
      // 处理容器大小变化
      const rect = this.$refs.canvas.getBoundingClientRect()
      this.$emit('resize', { width: rect.width, height: rect.height })
    },
    handleMouseDown(e) {
      if (this.readOnly) return

      // 画布拖动
      if (e.target === this.$refs.canvas) {
        this.isPanning = true
        this.panStart = { x: e.clientX, y: e.clientY }
        document.addEventListener('mousemove', this.handleMouseMove)
        document.addEventListener('mouseup', this.handleMouseUp)
      }

      // 清除选中状态
      this.graph.clearSelection()
      this.$emit('selection-change', { nodes: [], edges: [] })
    },
    handleNodeMouseDown(e, node) {
      if (this.readOnly) return

      e.stopPropagation()
      this.draggingNode = node
      this.graph.selectNode(node.id)
      this.$emit('selection-change', {
        nodes: this.graph.getSelectedNodes(),
        edges: this.graph.getSelectedEdges()
      })

      document.addEventListener('mousemove', this.handleMouseMove)
      document.addEventListener('mouseup', this.handleMouseUp)
    },
    handleAnchorMouseDown(e, node, anchor) {
      if (this.readOnly) return

      e.stopPropagation()
      this.startAnchor = anchor
      this.draggingEdge = null

      document.addEventListener('mousemove', this.handleMouseMove)
      document.addEventListener('mouseup', this.handleMouseUp)
    },
    handleEdgeMouseDown(e, edge) {
      if (this.readOnly) return

      e.stopPropagation()
      this.graph.selectEdge(edge.id)
      this.$emit('selection-change', {
        nodes: this.graph.getSelectedNodes(),
        edges: this.graph.getSelectedEdges()
      })
    },
    handleMouseMove(e) {
      if (this.draggingNode) {
        // 节点拖动
        const rect = this.$refs.canvas.getBoundingClientRect()
        let x = e.clientX - rect.left
        let y = e.clientY - rect.top

        // 根据snapToGrid属性决定是否对齐网格
        if (this.snapToGrid) {
          x = Math.round(x / this.gridSize) * this.gridSize
          y = Math.round(y / this.gridSize) * this.gridSize
        }

        this.graph.updateNodePosition(this.draggingNode.id, x, y)
        this.$emit('node-move', this.draggingNode)
      } else if (this.startAnchor && !this.draggingEdge) {
        // 开始创建边
        const rect = this.$refs.canvas.getBoundingClientRect()
        const x = e.clientX - rect.left
        const y = e.clientY - rect.top

        this.draggingEdge = {
          id: `temp-edge-${Date.now()}`,
          sourceNodeId: this.startAnchor.nodeId,
          sourceAnchorId: this.startAnchor.id,
          targetX: x,
          targetY: y,
          color: '#666',
          width: 2,
          showArrow: true
        }
      } else if (this.draggingEdge) {
        // 边拖动中
        const rect = this.$refs.canvas.getBoundingClientRect()
        this.draggingEdge.targetX = e.clientX - rect.left
        this.draggingEdge.targetY = e.clientY - rect.top
      } else if (this.isPanning) {
        // 画布平移
        const dx = e.clientX - this.panStart.x
        const dy = e.clientY - this.panStart.y
        this.panStart = { x: e.clientX, y: e.clientY }
        this.graph.pan(dx, dy)
        this.$emit('pan', { dx, dy })
      }
    },
    handleMouseUp(e) {
      if (this.draggingNode) {
        this.draggingNode = null
      } else if (this.startAnchor) {
        // 结束创建边
        const rect = this.$refs.canvas.getBoundingClientRect()
        const x = e.clientX - rect.left
        const y = e.clientY - rect.top

        // 查找目标节点和锚点
        const targetNode = this.graph.getNodeAtPosition(x, y)
        if (targetNode) {
          const targetAnchor = targetNode.anchors[0] // 简单起见，选择第一个锚点
          const newEdge = new Edge({
            id: `edge-${Date.now()}`,
            sourceNodeId: this.startAnchor.nodeId,
            sourceAnchorId: this.startAnchor.id,
            targetNodeId: targetNode.id,
            targetAnchorId: targetAnchor.id,
            color: '#666',
            width: 2,
            showArrow: true
          })

          this.graph.addEdge(newEdge)
          this.$emit('edge-added', newEdge)
        }

        this.startAnchor = null
        this.draggingEdge = null
      } else if (this.isPanning) {
        this.isPanning = false
      }

      document.removeEventListener('mousemove', this.handleMouseMove)
      document.removeEventListener('mouseup', this.handleMouseUp)
    },
    // 添加节点
    addNode(options) {
      const node = new Node({
        id: options.id || `node-${Date.now()}`,
        x: options.x || 100,
        y: options.y || 100,
        width: options.width || 80,
        height: options.height || 40,
        label: options.label,
        type: options.type || 'default',
        color: options.color || '#f0f0f0',
        anchors: options.anchors || [
          new Anchor({ id: `anchor-${Date.now()}-1`, x: 0, y: 20 }),
          new Anchor({ id: `anchor-${Date.now()}-2`, x: 40, y: 0 }),
          new Anchor({ id: `anchor-${Date.now()}-3`, x: 80, y: 20 }),
          new Anchor({ id: `anchor-${Date.now()}-4`, x: 40, y: 40 })
        ]
      })

      this.graph.addNode(node)
      this.$emit('node-added', node)
      return node
    },
    // 删除节点
    removeNode(nodeId) {
      const removed = this.graph.removeNode(nodeId)
      if (removed) {
        this.$emit('node-removed', removed)
      }
      return removed
    },
    // 添加边
    addEdge(options) {
      const edge = new Edge({
        id: options.id || `edge-${Date.now()}`,
        sourceNodeId: options.sourceNodeId,
        sourceAnchorId: options.sourceAnchorId,
        targetNodeId: options.targetNodeId,
        targetAnchorId: options.targetAnchorId,
        color: options.color || '#666',
        width: options.width || 2,
        showArrow: options.showArrow !== undefined ? options.showArrow : true
      })

      this.graph.addEdge(edge)
      this.$emit('edge-added', edge)
      return edge
    },
    // 删除边
    removeEdge(edgeId) {
      const removed = this.graph.removeEdge(edgeId)
      if (removed) {
        this.$emit('edge-removed', removed)
      }
      return removed
    },
    // 缩放
    zoom(factor) {
      this.scale = Math.max(0.5, Math.min(2, this.scale * factor))
      this.graph.zoom(this.scale)
      this.$emit('zoom', this.scale)
    },
    // 居中显示
    fitView() {
      this.graph.fitView()
      this.$emit('fit-view')
    },
    // 清空画布
    clear() {
      this.graph.clear()
      this.$emit('clear')
    }
  }
}
</script>

<style scoped>
.vue-topology {
  position: relative;
  overflow: hidden;
  border: 1px solid #e8e8e8;
}

.topology-canvas {
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #fff;
}

.nodes-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.node {
  position: absolute;
  border: 1px solid #999;
  border-radius: 4px;
  cursor: move;
  pointer-events: auto;
  transition: all 0.2s;
}

.node.selected {
  border-color: #1890ff;
  box-shadow: 0 0 8px rgba(24, 144, 255, 0.5);
}

.node-content {
  padding: 8px;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.anchor {
  position: absolute;
  width: 10px;
  height: 10px;
  background-color: #1890ff;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  cursor: crosshair;
  z-index: 10;
}

.edges-container {
  position: absolute;
  top: 0;
  left: 0;
  /* 移除pointer-events: none;，允许边接收事件 */
  /* pointer-events: none; */
}

.edges-container path {
  pointer-events: auto;
}
</style>